This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.
Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.
suppressMessages(library(tidyverse))
suppressMessages(library(stringr))
suppressMessages(library(ISLR))
suppressMessages(library(caret))
suppressMessages(library(doMC))
registerDoMC(cores=4)
Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.
When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).
Getting and proccesing the data
library(stringr)
myData = read.csv('/home/harpo/Dropbox/ongoing-work/git-repos/labeling-datasets/phd/datasets/data_all_result.txt', stringsAsFactors = F, sep = ' ')
#Create data backup
myData.bkup <- myData
#Create new column: length of model, and number of periodicity, duration and size characteristic in the model.
myData = myData %>% mutate(letter_count = nchar(State))
#Periodicity
myData = myData %>% mutate(strong_p = str_count(State,'[a-i]'))
myData = myData %>% mutate(weak_p = str_count(State,'[A-I]'))
myData = myData %>% mutate(weak_np = str_count(State,'[r-z]'))
myData = myData %>% mutate(strong_np = str_count(State,'[R-Z]'))
#Duration
myData = myData %>% mutate(duration_s = str_count(State,'(a|A|r|R|1|d|D|u|U|4|g|G|x|X|7)'))#c('a','A','r','R','1','d','D','u','U','4','g','G','x','X','7')))
myData = myData %>% mutate(duration_m = str_count(State,'(b|B|s|S|2|e|E|v|V|5|h|H|y|Y|8)'))#c('b','B','s','S','2','e','E','v','V','5','h','H','y','Y','8')))
myData = myData %>% mutate(duration_l = str_count(State,'(c|C|t|T|3|f|F|w|W|6|i|I|z|Z|9)'))#c('c','C','t','T','3','f','F','w','W','6','i','I','z','Z','9')))
#Size
myData = myData %>% mutate(size_s = str_count(State,'[a-c]') + str_count(State,'[A-C]') + str_count(State,'[r-t]') + str_count(State,'[R-T]') + str_count(State,'[1-3]'))
myData = myData %>% mutate(size_m = str_count(State,'[d-f]') + str_count(State,'[D-F]') + str_count(State,'[u-w]') + str_count(State,'[U-W]') + str_count(State,'[4-6]'))
myData = myData %>% mutate(size_l = str_count(State,'[g-i]') + str_count(State,'[G-I]') + str_count(State,'[x-z]') + str_count(State,'[X-Z]') + str_count(State,'[7-9]'))
#Remove from LabelName unnecessary characters (ej: V42, -17)
myData <- myData %>% mutate(LabelName = gsub('V[0-9]+-','',LabelName))
myData <- myData %>% mutate(LabelName = gsub('-[0-9]+','',LabelName))
myData <- myData %>% mutate(LabelName = gsub('CC[0-9]+-','CC-',LabelName))
#Keep only connection with more than 3 symbols
myData <- myData %>% filter(letter_count > 3)
#Periodicity %
myData <- myData %>% mutate(strong_p = (strong_p / letter_count))
myData <- myData %>% mutate(weak_p = (weak_p / letter_count))
myData <- myData %>% mutate(strong_np = (strong_np / letter_count))
myData <- myData %>% mutate(weak_np = (weak_np / letter_count))
#Duration %
myData <- myData %>% mutate(duration_s = (duration_s / letter_count))
myData <- myData %>% mutate(duration_m = (duration_m / letter_count))
myData <- myData %>% mutate(duration_l = (duration_l / letter_count))
#Size %
myData <- myData %>% mutate(size_s = (size_s / letter_count))
myData <- myData %>% mutate(size_m = (size_m / letter_count))
myData <- myData %>% mutate(size_l = (size_l / letter_count))
#head(myData)
myData[1:20,]
#Making feature vectors
feature_vectors = myData[,c('strong_p','weak_p','weak_np','strong_np','duration_s','duration_m','duration_l','size_s','size_m','size_l','letter_count','Label','LabelName')]
names(feature_vectors) = c("sp","wp","wnp","snp","ds","dm","dl","ss","sm","sl","length","class","subclass")
feature_vectors$class = factor(feature_vectors$class)
feature_vectors$subclass = factor(feature_vectors$subclass)
Create training set and testset
Random Forest Classificator
# Random Forest
rfFit <- train(class ~ .,
data = training,
metric="ROC",
method = "rf",
trControl = ctrl_fast)
rfFit
rfFit$finalModel
predsrfprobs=predict(rfFit,testing,type='prob')
predsrf=ifelse(predsrfprobs$Botnet >=0.9,'Botnet','Normal')
confusionMatrix(predsrf,testing$class)
library(ggplot2)
library(plotROC)
selectedIndices <- rfFit$pred$mtry == 2
ggplot(cbind(predsrfprobs,class=testing$class),
aes(m = Botnet, d = factor(class, labels=c("Normal","Botnet"),levels = c("Normal", "Botnet")))) +
geom_roc(hjust = -0.4, vjust = 1.5,colour='orange') +
theme_bw()
cbind(predsrfprobs,class=testing$class)
KNN
#Checking distibution in origanl data and partitioned data
prop.table(table(training$class)) * 100
prop.table(table(testing$class)) * 100
prop.table(table(feature_vectors$class)) * 100
trainX <- training[,names(training) != "class"]
preProcValues <- preProcess(x = trainX,method = c("center", "scale"))
preProcValues
knnFit <- train(class ~ ., data = training, method = "knn", trControl = ctrl_fast, preProcess = c("center","scale"), tuneLength = 20)
#Output of kNN fit
knnFit
#Plotting yields Number of Neighbours Vs accuracy (based on repeated cross validation)
plot(knnFit)
knnPredict <- predict(knnFit,newdata = testing )
#Get the confusion matrix to see accuracy value and other parameter values
confusionMatrix(knnPredict, testing$class )
mean(knnPredict == testing$class)
library(pROC)
knnPredict <- predict(knnFit,newdata = testing , type="prob")
knnROC <- roc(testing$class,knnPredict[,"Botnet"], levels = c('Normal','Botnet'))#rev(testing$class))
knnROC
ggplot(cbind(knnPredict,class=testing$class),
aes(m = Botnet, d = factor(class, labels=c("Normal","Botnet"),levels = c("Normal", "Botnet")))) +
geom_roc(hjust = -0.4, vjust = 1.5,colour='orange') +
theme_bw()
#plot(knnROC, type="S", print.thres= 0.5)
Logistic Regression
logicRFit <- train(class ~ ., method='glm', trControl = ctrl_fast,preProcess=c('scale', 'center'), data=training, family=binomial(link='logit'))
#logicRFit <- train(class ~ sp*wp*wnp*snp*ds*dm*dl*ss*sm*sl, method='glm', trControl = ctrl_fast,preProcess=c('scale', 'center'), data=training, family=binomial(link='logit'))
#logicRFit <- train(class ~ sp+wp+wnp+snp+ds+dm+dl+ss+sm+sl, method='glm', trControl = ctrl_fast,preProcess=c('scale', 'center'), data=training, family=binomial(link='logit'))
#summary(logicRFit)
#Output of Logistic Regression fit
logicRFit
logicRPredict <- predict(logicRFit, newdata = testing )
confusionMatrix(logicRPredict, testing$class)
logicRPredict <- predict(logicRFit, newdata = testing, type="prob")
logicROC <- roc(testing$class,logicRPredict[,"Botnet"], levels = c('Normal','Botnet'))#rev(testing$class))
ggplot(cbind(logicRPredict,class=testing$class),
aes(m = Botnet, d = factor(class, labels=c("Normal","Botnet"),levels = c("Normal", "Botnet")))) +
geom_roc(hjust = -0.4, vjust = 1.5,colour='orange') +
theme_bw()
#logicROC
Naive Bayes
naiveBayesFit <- train(class ~ ., method='nb', trControl = ctrl_fast,preProcess=c('scale', 'center'), data=training)
naiveBayesFit
naiveBayesPredict <- predict(naiveBayesFit, newdata = testing)
confusionMatrix(naiveBayesPredict, testing$class)
naiveBayesPredict <- predict(naiveBayesFit, newdata = testing, type = 'prob')
naiveBayesROC <- roc(testing$class,naiveBayesPredict[,"Botnet"], levels = c('Normal','Botnet'))#rev(testing$class))
naiveBayesROC
ggplot(cbind(naiveBayesPredict,class=testing$class),
aes(m = Botnet, d = factor(class, labels=c("Normal","Botnet"),levels = c("Normal", "Botnet")))) +
geom_roc(hjust = -0.4, vjust = 1.5,colour='orange') +
theme_bw()
#plot(naiveBayesROC, type="S", print.thres= 0.5)
Suport Vector Machine
svmFit <- train(class ~ ., method='svmLinear', trControl = ctrl_fast,preProcess=c('scale', 'center'), data=training, family=binomial(link='logit'))
svmFit
svmPredict <- predict(svmFit, newdata = testing)
confusionMatrix(svmPredict, testing$class)
svmPredict <- predict(svmFit, newdata = testing, type = "prob")
svmROC <- roc(testing$class,svmPredict[,"Botnet"], levels = c('Normal','Botnet'))#rev(testing$class))
svmROC
ggplot(cbind(svmPredict,class=testing$class),
aes(m = Botnet, d = factor(class, labels=c("Normal","Botnet"),levels = c("Normal", "Botnet")))) +
geom_roc(hjust = -0.4, vjust = 1.5,colour='orange') +
theme_bw()
Comparing Models
resamps <- resamples(list(rf = rfFit, lr = logicRFit, nv = naiveBayesFit, svm = svmFit))
summary(resamps)
bwplot(resamps)
diffs <- diff(resamps)
summary(diffs)
values=resamps$values
values
names(values)[2]<-"rfSens"
ggplot(values)+
geom_boxplot(aes(y=rfSens,x=1))
Making probabilistic table.
# Botnet probabilistic table
botnet_prob_result = data.frame(testing$class, predsrfprobs$Botnet, knnPredict$Botnet, logicRPredict$Botnet, naiveBayesPredict$Botnet ,svmPredict$Botnet)
names(botnet_prob_result) = c('TrueClass','RamdomForest','KNN','LogisticRegression', 'NaiveBayes', 'SVM','subclass')
#load("./botnet_prob_results.Rda")
library(grid)
library(gridExtra)
botnet_prob_result %>% group_by(subclass) %>% summarise(n=n(),mean=mean(KNN),sd=sd(KNN)) %>% arrange(desc(n))
botnet_prob_result %>% group_by(subclass) %>% summarise(n=n(),mean=mean(NaiveBayes),sd=sd(NaiveBayes)) %>% arrange(desc(n))%>% top_n(10)
Selecting by sd
botnet_10_top<-botnet_prob_result %>% group_by(subclass) %>% summarise(n=n()) %>% arrange(desc(n))%>% top_n(10)
Selecting by n
botnet_10_top<-inner_join(botnet_10_top,botnet_prob_result,by="subclass")
knn_plot<-bwplot(subclass~KNN,data=botnet_10_top,do.out = FALSE,scales=list(y=list(draw=FALSE)))
rf_plot<-bwplot(subclass~RamdomForest,data=botnet_10_top,do.out = FALSE,scales=list(y=list(draw=FALSE)))
rl_plot<-bwplot(subclass~LogisticRegression,data=botnet_10_top,do.out = FALSE,scales=list(y=list(draw=FALSE)))
svm_plot<-bwplot(subclass~SVM,data=botnet_10_top,do.out = FALSE,scales=list(y=list(draw=FALSE)))
pl = list(knn_plot, rf_plot,rl_plot,svm_plot)
# do.call(grid.arrange, c(pl, nrow=1))
do.call(grid.arrange, c(lapply(pl, update), list(nrow=1)))

some heatmaps

Clustering and PCA
kmeans_mod<-kmeans(testing[,1:10],centers = 10)
testing_cluster<-cbind(testing,cluster=kmeans_mod$cluster)
pca<-prcomp(testing[,c(-11,-12)], center = TRUE, scale. = TRUE)
pca_testing<-data.frame(pca$x,class=testing_cluster$class,
subclass=testing_cluster$subclass,
cluster=testing_cluster$cluster
)
g<-ggplot(pca_testing,aes(x=PC1,y=PC2))+
geom_jitter(aes(color=as.factor(subclass),text=cluster,shape=class))+
#geom_point(aes(shape=asignacion),size=3)+
ylab("PC1")+xlab("PC2")+
theme_classic()+
#scale_shape_manual(values=c(8,6))+
guides(color=FALSE,alpha=FALSE)
Ignoring unknown aesthetics: text
ggplotly(g)
#Normal probabilistic table
normal_prob_result = data.frame(testing$class, predsrfprobs$Normal, knnPredict$Normal, logicRPredict$Normal, naiveBayesPredict$Normal ,svmPredict$Normal)
names(normal_prob_result) = c('True Class','Ramdom Forest','KNN','Logistic Regression', 'Naive Bayes', 'Suport VM')
normal_prob_result
LS0tCnRpdGxlOiAiQ29ubmVjdGlvbiBDbGFzc2lmaWNhdGlvbiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciouIAoKYGBge3J9CnN1cHByZXNzTWVzc2FnZXMobGlicmFyeSh0aWR5dmVyc2UpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoc3RyaW5ncikpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShJU0xSKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGNhcmV0KSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGRvTUMpKQpsaWJyYXJ5KHBsb3RseSkKcmVnaXN0ZXJEb01DKGNvcmVzPTQpCgpgYGAKCkFkZCBhIG5ldyBjaHVuayBieSBjbGlja2luZyB0aGUgKkluc2VydCBDaHVuayogYnV0dG9uIG9uIHRoZSB0b29sYmFyIG9yIGJ5IHByZXNzaW5nICpDdHJsK0FsdCtJKi4KCldoZW4geW91IHNhdmUgdGhlIG5vdGVib29rLCBhbiBIVE1MIGZpbGUgY29udGFpbmluZyB0aGUgY29kZSBhbmQgb3V0cHV0IHdpbGwgYmUgc2F2ZWQgYWxvbmdzaWRlIGl0IChjbGljayB0aGUgKlByZXZpZXcqIGJ1dHRvbiBvciBwcmVzcyAqQ3RybCtTaGlmdCtLKiB0byBwcmV2aWV3IHRoZSBIVE1MIGZpbGUpLgoKIyMjIEdldHRpbmcgYW5kIHByb2NjZXNpbmcgdGhlIGRhdGEKYGBge3J9CmxpYnJhcnkoc3RyaW5ncikKbXlEYXRhID0gcmVhZC5jc3YoJy9ob21lL2hhcnBvL0Ryb3Bib3gvb25nb2luZy13b3JrL2dpdC1yZXBvcy9sYWJlbGluZy1kYXRhc2V0cy9waGQvZGF0YXNldHMvZGF0YV9hbGxfcmVzdWx0LnR4dCcsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGLCBzZXAgPSAnICcpCgojQ3JlYXRlIGRhdGEgYmFja3VwCm15RGF0YS5ia3VwIDwtIG15RGF0YQojQ3JlYXRlIG5ldyBjb2x1bW46IGxlbmd0aCBvZiBtb2RlbCwgYW5kIG51bWJlciBvZiBwZXJpb2RpY2l0eSwgZHVyYXRpb24gYW5kIHNpemUgY2hhcmFjdGVyaXN0aWMgaW4gdGhlIG1vZGVsLgpteURhdGEgPSBteURhdGEgJT4lIG11dGF0ZShsZXR0ZXJfY291bnQgPSBuY2hhcihTdGF0ZSkpCiNQZXJpb2RpY2l0eQpteURhdGEgPSBteURhdGEgJT4lIG11dGF0ZShzdHJvbmdfcCA9IHN0cl9jb3VudChTdGF0ZSwnW2EtaV0nKSkKbXlEYXRhID0gbXlEYXRhICU+JSBtdXRhdGUod2Vha19wID0gc3RyX2NvdW50KFN0YXRlLCdbQS1JXScpKQpteURhdGEgPSBteURhdGEgJT4lIG11dGF0ZSh3ZWFrX25wID0gc3RyX2NvdW50KFN0YXRlLCdbci16XScpKQpteURhdGEgPSBteURhdGEgJT4lIG11dGF0ZShzdHJvbmdfbnAgPSBzdHJfY291bnQoU3RhdGUsJ1tSLVpdJykpCiNEdXJhdGlvbgpteURhdGEgPSBteURhdGEgJT4lIG11dGF0ZShkdXJhdGlvbl9zID0gc3RyX2NvdW50KFN0YXRlLCcoYXxBfHJ8UnwxfGR8RHx1fFV8NHxnfEd8eHxYfDcpJykpI2MoJ2EnLCdBJywncicsJ1InLCcxJywnZCcsJ0QnLCd1JywnVScsJzQnLCdnJywnRycsJ3gnLCdYJywnNycpKSkKbXlEYXRhID0gbXlEYXRhICU+JSBtdXRhdGUoZHVyYXRpb25fbSA9IHN0cl9jb3VudChTdGF0ZSwnKGJ8QnxzfFN8MnxlfEV8dnxWfDV8aHxIfHl8WXw4KScpKSNjKCdiJywnQicsJ3MnLCdTJywnMicsJ2UnLCdFJywndicsJ1YnLCc1JywnaCcsJ0gnLCd5JywnWScsJzgnKSkpCm15RGF0YSA9IG15RGF0YSAlPiUgbXV0YXRlKGR1cmF0aW9uX2wgPSBzdHJfY291bnQoU3RhdGUsJyhjfEN8dHxUfDN8ZnxGfHd8V3w2fGl8SXx6fFp8OSknKSkjYygnYycsJ0MnLCd0JywnVCcsJzMnLCdmJywnRicsJ3cnLCdXJywnNicsJ2knLCdJJywneicsJ1onLCc5JykpKQojU2l6ZQpteURhdGEgPSBteURhdGEgJT4lIG11dGF0ZShzaXplX3MgPSBzdHJfY291bnQoU3RhdGUsJ1thLWNdJykgKyBzdHJfY291bnQoU3RhdGUsJ1tBLUNdJykgKyBzdHJfY291bnQoU3RhdGUsJ1tyLXRdJykgKyBzdHJfY291bnQoU3RhdGUsJ1tSLVRdJykgKyBzdHJfY291bnQoU3RhdGUsJ1sxLTNdJykpCm15RGF0YSA9IG15RGF0YSAlPiUgbXV0YXRlKHNpemVfbSA9IHN0cl9jb3VudChTdGF0ZSwnW2QtZl0nKSArIHN0cl9jb3VudChTdGF0ZSwnW0QtRl0nKSArIHN0cl9jb3VudChTdGF0ZSwnW3Utd10nKSArIHN0cl9jb3VudChTdGF0ZSwnW1UtV10nKSArIHN0cl9jb3VudChTdGF0ZSwnWzQtNl0nKSkKbXlEYXRhID0gbXlEYXRhICU+JSBtdXRhdGUoc2l6ZV9sID0gc3RyX2NvdW50KFN0YXRlLCdbZy1pXScpICsgc3RyX2NvdW50KFN0YXRlLCdbRy1JXScpICsgc3RyX2NvdW50KFN0YXRlLCdbeC16XScpICsgc3RyX2NvdW50KFN0YXRlLCdbWC1aXScpICsgc3RyX2NvdW50KFN0YXRlLCdbNy05XScpKQoKI1JlbW92ZSBmcm9tIExhYmVsTmFtZSB1bm5lY2Vzc2FyeSBjaGFyYWN0ZXJzIChlajogVjQyLCAtMTcpCm15RGF0YSA8LSBteURhdGEgJT4lIG11dGF0ZShMYWJlbE5hbWUgPSBnc3ViKCdWWzAtOV0rLScsJycsTGFiZWxOYW1lKSkKbXlEYXRhIDwtIG15RGF0YSAlPiUgbXV0YXRlKExhYmVsTmFtZSA9IGdzdWIoJy1bMC05XSsnLCcnLExhYmVsTmFtZSkpCm15RGF0YSA8LSBteURhdGEgJT4lIG11dGF0ZShMYWJlbE5hbWUgPSBnc3ViKCdDQ1swLTldKy0nLCdDQy0nLExhYmVsTmFtZSkpCgojS2VlcCBvbmx5IGNvbm5lY3Rpb24gd2l0aCBtb3JlIHRoYW4gMyBzeW1ib2xzCm15RGF0YSA8LSBteURhdGEgJT4lIGZpbHRlcihsZXR0ZXJfY291bnQgPiAzKQoKI1BlcmlvZGljaXR5ICUKbXlEYXRhIDwtIG15RGF0YSAlPiUgbXV0YXRlKHN0cm9uZ19wID0gKHN0cm9uZ19wIC8gbGV0dGVyX2NvdW50KSkKbXlEYXRhIDwtIG15RGF0YSAlPiUgbXV0YXRlKHdlYWtfcCA9ICh3ZWFrX3AgLyBsZXR0ZXJfY291bnQpKQpteURhdGEgPC0gbXlEYXRhICU+JSBtdXRhdGUoc3Ryb25nX25wID0gKHN0cm9uZ19ucCAvIGxldHRlcl9jb3VudCkpCm15RGF0YSA8LSBteURhdGEgJT4lIG11dGF0ZSh3ZWFrX25wID0gKHdlYWtfbnAgLyBsZXR0ZXJfY291bnQpKQojRHVyYXRpb24gJQpteURhdGEgPC0gbXlEYXRhICU+JSBtdXRhdGUoZHVyYXRpb25fcyA9IChkdXJhdGlvbl9zIC8gbGV0dGVyX2NvdW50KSkKbXlEYXRhIDwtIG15RGF0YSAlPiUgbXV0YXRlKGR1cmF0aW9uX20gPSAoZHVyYXRpb25fbSAvIGxldHRlcl9jb3VudCkpCm15RGF0YSA8LSBteURhdGEgJT4lIG11dGF0ZShkdXJhdGlvbl9sID0gKGR1cmF0aW9uX2wgLyBsZXR0ZXJfY291bnQpKQojU2l6ZSAlCm15RGF0YSA8LSBteURhdGEgJT4lIG11dGF0ZShzaXplX3MgPSAoc2l6ZV9zIC8gbGV0dGVyX2NvdW50KSkKbXlEYXRhIDwtIG15RGF0YSAlPiUgbXV0YXRlKHNpemVfbSA9IChzaXplX20gLyBsZXR0ZXJfY291bnQpKQpteURhdGEgPC0gbXlEYXRhICU+JSBtdXRhdGUoc2l6ZV9sID0gKHNpemVfbCAvIGxldHRlcl9jb3VudCkpCgojaGVhZChteURhdGEpCm15RGF0YVsxOjIwLF0KCiNNYWtpbmcgZmVhdHVyZSB2ZWN0b3JzCmZlYXR1cmVfdmVjdG9ycyA9IG15RGF0YVssYygnc3Ryb25nX3AnLCd3ZWFrX3AnLCd3ZWFrX25wJywnc3Ryb25nX25wJywnZHVyYXRpb25fcycsJ2R1cmF0aW9uX20nLCdkdXJhdGlvbl9sJywnc2l6ZV9zJywnc2l6ZV9tJywnc2l6ZV9sJywnbGV0dGVyX2NvdW50JywnTGFiZWwnLCdMYWJlbE5hbWUnKV0KbmFtZXMoZmVhdHVyZV92ZWN0b3JzKSA9IGMoInNwIiwid3AiLCJ3bnAiLCJzbnAiLCJkcyIsImRtIiwiZGwiLCJzcyIsInNtIiwic2wiLCJsZW5ndGgiLCJjbGFzcyIsInN1YmNsYXNzIikKZmVhdHVyZV92ZWN0b3JzJGNsYXNzID0gZmFjdG9yKGZlYXR1cmVfdmVjdG9ycyRjbGFzcykKZmVhdHVyZV92ZWN0b3JzJHN1YmNsYXNzID0gZmFjdG9yKGZlYXR1cmVfdmVjdG9ycyRzdWJjbGFzcykKYGBgCgojIyMgQ3JlYXRlIHRyYWluaW5nIHNldCBhbmQgdGVzdHNldApgYGB7cn0Kc2V0LnNlZWQoMzAwKQp0cmFpbkluZGV4IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oZmVhdHVyZV92ZWN0b3JzJGNsYXNzLCBwPTAuODAsIGxpc3Q9RkFMU0UpCmRhdGFfdHJhaW4gPC0gZmVhdHVyZV92ZWN0b3JzWyB0cmFpbkluZGV4LF0KZGF0YV90ZXN0IDwtIGZlYXR1cmVfdmVjdG9yc1stdHJhaW5JbmRleCxdCgojZGF0YV90cmFpbiA9IGRhdGFfdHJhaW4gJT4lIGZpbHRlcihsZW5ndGg+NSkKdHJhaW4gPC0gdXBTYW1wbGUoeCA9IGRhdGFfdHJhaW4sICB5ID0gZGF0YV90cmFpbiRjbGFzcywgeW5hbWU9ImNsYXNzIikKI3RyYWluIDwtIHVwU2FtcGxlKHggPSB0cmFpbiwgIHkgPSB0cmFpbiRzdWJjbGFzcywgeW5hbWU9ImNsYXNzIikKdHJhaW5pbmcgPC0gdHJhaW5bLC1jKDExLDEyKV0KdGVzdGluZyA8LSBkYXRhX3Rlc3RbLC1jKDExKV0KdHJhaW5pbmcKCmN0cmxfZmFzdCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kPSJjdiIsIAogICAgICAgICAgICAgICAgICAgICByZXBlYXRzPTIsCiAgICAgICAgICAgICAgICAgICAgIG51bWJlcj0xMCwgCiAgICAgICAgICAgICAgICAgICAgIHN1bW1hcnlGdW5jdGlvbj10d29DbGFzc1N1bW1hcnksCiAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2VJdGVyPVQsCiAgICAgICAgICAgICAgICAgICAgIGNsYXNzUHJvYnM9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgYWxsb3dQYXJhbGxlbCA9IFRSVUUpICAKY3RybCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kPSJyZXBlYXRlZGN2IixyZXBlYXRzID0gMykgIyxjbGFzc1Byb2JzPVRSVUUsc3VtbWFyeUZ1bmN0aW9uID0gdHdvQ2xhc3NTdW1tYXJ5KQpgYGAKCgojIyMgUmFuZG9tIEZvcmVzdCBDbGFzc2lmaWNhdG9yCmBgYHtyfQogICMgUmFuZG9tIEZvcmVzdApyZkZpdCA8LSB0cmFpbihjbGFzcyB+IC4sCiAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbmluZywKICAgICAgICAgICAgICAgbWV0cmljPSJST0MiLAogICAgICAgICAgICAgICBtZXRob2QgPSAicmYiLAogICAgICAgICAgICAgICB0ckNvbnRyb2wgPSBjdHJsX2Zhc3QpCgpyZkZpdApyZkZpdCRmaW5hbE1vZGVsCmBgYAoKYGBge3J9CnByZWRzcmZwcm9icz1wcmVkaWN0KHJmRml0LHRlc3RpbmcsdHlwZT0ncHJvYicpCnByZWRzcmY9aWZlbHNlKHByZWRzcmZwcm9icyRCb3RuZXQgPj0wLjksJ0JvdG5ldCcsJ05vcm1hbCcpCmNvbmZ1c2lvbk1hdHJpeChwcmVkc3JmLHRlc3RpbmckY2xhc3MpCmBgYAoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwbG90Uk9DKQpzZWxlY3RlZEluZGljZXMgPC0gcmZGaXQkcHJlZCRtdHJ5ID09IDIKZ2dwbG90KGNiaW5kKHByZWRzcmZwcm9icyxjbGFzcz10ZXN0aW5nJGNsYXNzKSwgCiAgICAgICBhZXMobSA9IEJvdG5ldCwgZCA9IGZhY3RvcihjbGFzcywgbGFiZWxzPWMoIk5vcm1hbCIsIkJvdG5ldCIpLGxldmVscyA9IGMoIk5vcm1hbCIsICJCb3RuZXQiKSkpKSArIAogICAgZ2VvbV9yb2MoaGp1c3QgPSAtMC40LCB2anVzdCA9IDEuNSxjb2xvdXI9J29yYW5nZScpICsgCiAgdGhlbWVfYncoKQoKY2JpbmQocHJlZHNyZnByb2JzLGNsYXNzPXRlc3RpbmckY2xhc3MpCmBgYAoKCiMjIyBLTk4KYGBge3J9CiNDaGVja2luZyBkaXN0aWJ1dGlvbiBpbiBvcmlnYW5sIGRhdGEgYW5kIHBhcnRpdGlvbmVkIGRhdGEKcHJvcC50YWJsZSh0YWJsZSh0cmFpbmluZyRjbGFzcykpICogMTAwCnByb3AudGFibGUodGFibGUodGVzdGluZyRjbGFzcykpICogMTAwCnByb3AudGFibGUodGFibGUoZmVhdHVyZV92ZWN0b3JzJGNsYXNzKSkgKiAxMDAKCnRyYWluWCA8LSB0cmFpbmluZ1ssbmFtZXModHJhaW5pbmcpICE9ICJjbGFzcyJdCnByZVByb2NWYWx1ZXMgPC0gcHJlUHJvY2Vzcyh4ID0gdHJhaW5YLG1ldGhvZCA9IGMoImNlbnRlciIsICJzY2FsZSIpKQpwcmVQcm9jVmFsdWVzCmBgYApgYGB7cn0Ka25uRml0IDwtIHRyYWluKGNsYXNzIH4gLiwgZGF0YSA9IHRyYWluaW5nLCBtZXRob2QgPSAia25uIiwgdHJDb250cm9sID0gY3RybF9mYXN0LCBwcmVQcm9jZXNzID0gYygiY2VudGVyIiwic2NhbGUiKSwgdHVuZUxlbmd0aCA9IDIwKQoKI091dHB1dCBvZiBrTk4gZml0CmtubkZpdApgYGAKYGBge3J9CiNQbG90dGluZyB5aWVsZHMgTnVtYmVyIG9mIE5laWdoYm91cnMgVnMgYWNjdXJhY3kgKGJhc2VkIG9uIHJlcGVhdGVkIGNyb3NzIHZhbGlkYXRpb24pCnBsb3Qoa25uRml0KQpgYGAKYGBge3J9CmtublByZWRpY3QgPC0gcHJlZGljdChrbm5GaXQsbmV3ZGF0YSA9IHRlc3RpbmcgKQojR2V0IHRoZSBjb25mdXNpb24gbWF0cml4IHRvIHNlZSBhY2N1cmFjeSB2YWx1ZSBhbmQgb3RoZXIgcGFyYW1ldGVyIHZhbHVlcwpjb25mdXNpb25NYXRyaXgoa25uUHJlZGljdCwgdGVzdGluZyRjbGFzcyApCmBgYApgYGB7cn0KbWVhbihrbm5QcmVkaWN0ID09IHRlc3RpbmckY2xhc3MpCmBgYApgYGB7cn0KbGlicmFyeShwUk9DKQprbm5QcmVkaWN0IDwtIHByZWRpY3Qoa25uRml0LG5ld2RhdGEgPSB0ZXN0aW5nICwgdHlwZT0icHJvYiIpCmtublJPQyA8LSByb2ModGVzdGluZyRjbGFzcyxrbm5QcmVkaWN0WywiQm90bmV0Il0sIGxldmVscyA9IGMoJ05vcm1hbCcsJ0JvdG5ldCcpKSNyZXYodGVzdGluZyRjbGFzcykpCmtublJPQwpgYGAKCmBgYHtyfQpnZ3Bsb3QoY2JpbmQoa25uUHJlZGljdCxjbGFzcz10ZXN0aW5nJGNsYXNzKSwgCiAgICAgICBhZXMobSA9IEJvdG5ldCwgZCA9IGZhY3RvcihjbGFzcywgbGFiZWxzPWMoIk5vcm1hbCIsIkJvdG5ldCIpLGxldmVscyA9IGMoIk5vcm1hbCIsICJCb3RuZXQiKSkpKSArIAogICAgZ2VvbV9yb2MoaGp1c3QgPSAtMC40LCB2anVzdCA9IDEuNSxjb2xvdXI9J29yYW5nZScpICsgCiAgdGhlbWVfYncoKQoKI3Bsb3Qoa25uUk9DLCB0eXBlPSJTIiwgcHJpbnQudGhyZXM9IDAuNSkKYGBgCiMjIyBMb2dpc3RpYyBSZWdyZXNzaW9uCgpgYGB7cn0KbG9naWNSRml0IDwtIHRyYWluKGNsYXNzIH4gLiwgbWV0aG9kPSdnbG0nLCB0ckNvbnRyb2wgPSBjdHJsX2Zhc3QscHJlUHJvY2Vzcz1jKCdzY2FsZScsICdjZW50ZXInKSwgZGF0YT10cmFpbmluZywgZmFtaWx5PWJpbm9taWFsKGxpbms9J2xvZ2l0JykpCiNsb2dpY1JGaXQgPC0gdHJhaW4oY2xhc3MgfiBzcCp3cCp3bnAqc25wKmRzKmRtKmRsKnNzKnNtKnNsLCBtZXRob2Q9J2dsbScsIHRyQ29udHJvbCA9IGN0cmxfZmFzdCxwcmVQcm9jZXNzPWMoJ3NjYWxlJywgJ2NlbnRlcicpLCBkYXRhPXRyYWluaW5nLCBmYW1pbHk9Ymlub21pYWwobGluaz0nbG9naXQnKSkKI2xvZ2ljUkZpdCA8LSB0cmFpbihjbGFzcyB+IHNwK3dwK3ducCtzbnArZHMrZG0rZGwrc3Mrc20rc2wsIG1ldGhvZD0nZ2xtJywgdHJDb250cm9sID0gY3RybF9mYXN0LHByZVByb2Nlc3M9Yygnc2NhbGUnLCAnY2VudGVyJyksIGRhdGE9dHJhaW5pbmcsIGZhbWlseT1iaW5vbWlhbChsaW5rPSdsb2dpdCcpKQoKI3N1bW1hcnkobG9naWNSRml0KQojT3V0cHV0IG9mIExvZ2lzdGljIFJlZ3Jlc3Npb24gZml0CmxvZ2ljUkZpdApgYGAKCmBgYHtyfQoKbG9naWNSUHJlZGljdCA8LSBwcmVkaWN0KGxvZ2ljUkZpdCwgbmV3ZGF0YSA9IHRlc3RpbmcgKQoKY29uZnVzaW9uTWF0cml4KGxvZ2ljUlByZWRpY3QsIHRlc3RpbmckY2xhc3MpCmBgYApgYGB7cn0KbG9naWNSUHJlZGljdCA8LSBwcmVkaWN0KGxvZ2ljUkZpdCwgbmV3ZGF0YSA9IHRlc3RpbmcsIHR5cGU9InByb2IiKQpsb2dpY1JPQyA8LSByb2ModGVzdGluZyRjbGFzcyxsb2dpY1JQcmVkaWN0WywiQm90bmV0Il0sIGxldmVscyA9IGMoJ05vcm1hbCcsJ0JvdG5ldCcpKSNyZXYodGVzdGluZyRjbGFzcykpCgpnZ3Bsb3QoY2JpbmQobG9naWNSUHJlZGljdCxjbGFzcz10ZXN0aW5nJGNsYXNzKSwgCiAgICAgICBhZXMobSA9IEJvdG5ldCwgZCA9IGZhY3RvcihjbGFzcywgbGFiZWxzPWMoIk5vcm1hbCIsIkJvdG5ldCIpLGxldmVscyA9IGMoIk5vcm1hbCIsICJCb3RuZXQiKSkpKSArIAogICAgZ2VvbV9yb2MoaGp1c3QgPSAtMC40LCB2anVzdCA9IDEuNSxjb2xvdXI9J29yYW5nZScpICsgCiAgdGhlbWVfYncoKQoKI2xvZ2ljUk9DCmBgYAoKIyMjIE5haXZlIEJheWVzCmBgYHtyfQpuYWl2ZUJheWVzRml0IDwtIHRyYWluKGNsYXNzIH4gLiwgbWV0aG9kPSduYicsIHRyQ29udHJvbCA9IGN0cmxfZmFzdCxwcmVQcm9jZXNzPWMoJ3NjYWxlJywgJ2NlbnRlcicpLCBkYXRhPXRyYWluaW5nKQpuYWl2ZUJheWVzRml0CmBgYAoKYGBge3J9Cm5haXZlQmF5ZXNQcmVkaWN0IDwtIHByZWRpY3QobmFpdmVCYXllc0ZpdCwgbmV3ZGF0YSA9IHRlc3RpbmcpCgpjb25mdXNpb25NYXRyaXgobmFpdmVCYXllc1ByZWRpY3QsIHRlc3RpbmckY2xhc3MpCmBgYApgYGB7cn0KbmFpdmVCYXllc1ByZWRpY3QgPC0gcHJlZGljdChuYWl2ZUJheWVzRml0LCBuZXdkYXRhID0gdGVzdGluZywgdHlwZSA9ICdwcm9iJykKbmFpdmVCYXllc1JPQyA8LSByb2ModGVzdGluZyRjbGFzcyxuYWl2ZUJheWVzUHJlZGljdFssIkJvdG5ldCJdLCBsZXZlbHMgPSBjKCdOb3JtYWwnLCdCb3RuZXQnKSkjcmV2KHRlc3RpbmckY2xhc3MpKQpuYWl2ZUJheWVzUk9DCgpnZ3Bsb3QoY2JpbmQobmFpdmVCYXllc1ByZWRpY3QsY2xhc3M9dGVzdGluZyRjbGFzcyksIAogICAgICAgYWVzKG0gPSBCb3RuZXQsIGQgPSBmYWN0b3IoY2xhc3MsIGxhYmVscz1jKCJOb3JtYWwiLCJCb3RuZXQiKSxsZXZlbHMgPSBjKCJOb3JtYWwiLCAiQm90bmV0IikpKSkgKyAKICAgIGdlb21fcm9jKGhqdXN0ID0gLTAuNCwgdmp1c3QgPSAxLjUsY29sb3VyPSdvcmFuZ2UnKSArIAogIHRoZW1lX2J3KCkKCiNwbG90KG5haXZlQmF5ZXNST0MsIHR5cGU9IlMiLCBwcmludC50aHJlcz0gMC41KQpgYGAKCgojIyMgU3Vwb3J0IFZlY3RvciBNYWNoaW5lCmBgYHtyfQpzdm1GaXQgPC0gdHJhaW4oY2xhc3MgfiAuLCBtZXRob2Q9J3N2bUxpbmVhcicsIHRyQ29udHJvbCA9IGN0cmxfZmFzdCxwcmVQcm9jZXNzPWMoJ3NjYWxlJywgJ2NlbnRlcicpLCBkYXRhPXRyYWluaW5nLCBmYW1pbHk9Ymlub21pYWwobGluaz0nbG9naXQnKSkKc3ZtRml0CmBgYAoKYGBge3J9CnN2bVByZWRpY3QgPC0gcHJlZGljdChzdm1GaXQsIG5ld2RhdGEgPSB0ZXN0aW5nKQpjb25mdXNpb25NYXRyaXgoc3ZtUHJlZGljdCwgdGVzdGluZyRjbGFzcykKYGBgCgpgYGB7cn0Kc3ZtUHJlZGljdCA8LSBwcmVkaWN0KHN2bUZpdCwgbmV3ZGF0YSA9IHRlc3RpbmcsIHR5cGUgPSAicHJvYiIpCgpzdm1ST0MgPC0gcm9jKHRlc3RpbmckY2xhc3Msc3ZtUHJlZGljdFssIkJvdG5ldCJdLCBsZXZlbHMgPSBjKCdOb3JtYWwnLCdCb3RuZXQnKSkjcmV2KHRlc3RpbmckY2xhc3MpKQpzdm1ST0MKCmdncGxvdChjYmluZChzdm1QcmVkaWN0LGNsYXNzPXRlc3RpbmckY2xhc3MpLCAKICAgICAgIGFlcyhtID0gQm90bmV0LCBkID0gZmFjdG9yKGNsYXNzLCBsYWJlbHM9YygiTm9ybWFsIiwiQm90bmV0IiksbGV2ZWxzID0gYygiTm9ybWFsIiwgIkJvdG5ldCIpKSkpICsgCiAgICBnZW9tX3JvYyhoanVzdCA9IC0wLjQsIHZqdXN0ID0gMS41LGNvbG91cj0nb3JhbmdlJykgKyAKICB0aGVtZV9idygpCgpgYGAKCgojIyMgQ29tcGFyaW5nIE1vZGVscwpgYGB7cn0KcmVzYW1wcyA8LSByZXNhbXBsZXMobGlzdChyZiA9IHJmRml0LCBsciA9IGxvZ2ljUkZpdCwgbnYgPSBuYWl2ZUJheWVzRml0LCBzdm0gPSBzdm1GaXQpKQpzdW1tYXJ5KHJlc2FtcHMpCmJ3cGxvdChyZXNhbXBzKQpkaWZmcyA8LSBkaWZmKHJlc2FtcHMpCnN1bW1hcnkoZGlmZnMpCnZhbHVlcz1yZXNhbXBzJHZhbHVlcwp2YWx1ZXMKbmFtZXModmFsdWVzKVsyXTwtInJmU2VucyIKCmdncGxvdCh2YWx1ZXMpKwogIGdlb21fYm94cGxvdChhZXMoeT1yZlNlbnMseD0xKSkKCmBgYAojIyMgTWFraW5nIHByb2JhYmlsaXN0aWMgdGFibGUuCmBgYHtyfQojIEJvdG5ldCBwcm9iYWJpbGlzdGljIHRhYmxlCmJvdG5ldF9wcm9iX3Jlc3VsdCA9IGRhdGEuZnJhbWUodGVzdGluZyRjbGFzcywgcHJlZHNyZnByb2JzJEJvdG5ldCwga25uUHJlZGljdCRCb3RuZXQsIGxvZ2ljUlByZWRpY3QkQm90bmV0LCBuYWl2ZUJheWVzUHJlZGljdCRCb3RuZXQgLHN2bVByZWRpY3QkQm90bmV0KQpuYW1lcyhib3RuZXRfcHJvYl9yZXN1bHQpID0gYygnVHJ1ZUNsYXNzJywnUmFtZG9tRm9yZXN0JywnS05OJywnTG9naXN0aWNSZWdyZXNzaW9uJywgJ05haXZlQmF5ZXMnLCAnU1ZNJywnc3ViY2xhc3MnKQpgYGAKCgpgYGB7cn0KI2xvYWQoIi4vYm90bmV0X3Byb2JfcmVzdWx0cy5SZGEiKQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoZ3JpZEV4dHJhKQpib3RuZXRfcHJvYl9yZXN1bHQgJT4lIGdyb3VwX2J5KHN1YmNsYXNzKSAlPiUgc3VtbWFyaXNlKG49bigpLG1lYW49bWVhbihLTk4pLHNkPXNkKEtOTikpICU+JSBhcnJhbmdlKGRlc2MobikpCmJvdG5ldF9wcm9iX3Jlc3VsdCAlPiUgZ3JvdXBfYnkoc3ViY2xhc3MpICU+JSBzdW1tYXJpc2Uobj1uKCksbWVhbj1tZWFuKE5haXZlQmF5ZXMpLHNkPXNkKE5haXZlQmF5ZXMpKSAlPiUgYXJyYW5nZShkZXNjKG4pKSU+JSB0b3BfbigxMCkKYm90bmV0XzEwX3RvcDwtYm90bmV0X3Byb2JfcmVzdWx0ICU+JSBncm91cF9ieShzdWJjbGFzcykgJT4lIHN1bW1hcmlzZShuPW4oKSkgJT4lIGFycmFuZ2UoZGVzYyhuKSklPiUgdG9wX24oMTApCmJvdG5ldF8xMF90b3A8LWlubmVyX2pvaW4oYm90bmV0XzEwX3RvcCxib3RuZXRfcHJvYl9yZXN1bHQsYnk9InN1YmNsYXNzIikKCmtubl9wbG90PC1id3Bsb3Qoc3ViY2xhc3N+S05OLGRhdGE9Ym90bmV0XzEwX3RvcCxkby5vdXQgPSBGQUxTRSxzY2FsZXM9bGlzdCh5PWxpc3QoZHJhdz1GQUxTRSkpKQpyZl9wbG90PC1id3Bsb3Qoc3ViY2xhc3N+UmFtZG9tRm9yZXN0LGRhdGE9Ym90bmV0XzEwX3RvcCxkby5vdXQgPSBGQUxTRSxzY2FsZXM9bGlzdCh5PWxpc3QoZHJhdz1GQUxTRSkpKQpybF9wbG90PC1id3Bsb3Qoc3ViY2xhc3N+TG9naXN0aWNSZWdyZXNzaW9uLGRhdGE9Ym90bmV0XzEwX3RvcCxkby5vdXQgPSBGQUxTRSxzY2FsZXM9bGlzdCh5PWxpc3QoZHJhdz1GQUxTRSkpKQpzdm1fcGxvdDwtYndwbG90KHN1YmNsYXNzflNWTSxkYXRhPWJvdG5ldF8xMF90b3AsZG8ub3V0ID0gRkFMU0Usc2NhbGVzPWxpc3QoeT1saXN0KGRyYXc9RkFMU0UpKSkKCgpwbCA9IGxpc3Qoa25uX3Bsb3QsIHJmX3Bsb3QscmxfcGxvdCxzdm1fcGxvdCkKIyBkby5jYWxsKGdyaWQuYXJyYW5nZSwgYyhwbCwgbnJvdz0xKSkKZG8uY2FsbChncmlkLmFycmFuZ2UsIGMobGFwcGx5KHBsLCB1cGRhdGUpLCBsaXN0KG5yb3c9MSkpKQoKYGBgCgojIyMjIHNvbWUgaGVhdG1hcHMKYGBge3IgaGVhdG1hcHN9CmxpYnJhcnkoc2NhbGVzKQprbm5fbTwtbWF0cml4KGJvdG5ldF9wcm9iX3Jlc3VsdFsxOjE4MzAsXSRLTk4sbmNvbD0zMCxucm93PTYxKQpzdm1fbTwtbWF0cml4KGJvdG5ldF9wcm9iX3Jlc3VsdFsxOjE4MzAsXSRTVk0sbmNvbD0zMCxucm93PTYxKQpscl9tPC1tYXRyaXgoYm90bmV0X3Byb2JfcmVzdWx0WzE6MTgzMCxdJExvZ2lzdGljUmVncmVzc2lvbixuY29sPTMwLG5yb3c9NjEpCm5iX208LW1hdHJpeChib3RuZXRfcHJvYl9yZXN1bHRbMToxODMwLF0kTmFpdmVCYXllcyxuY29sPTMwLG5yb3c9NjEpCnJmX208LW1hdHJpeChib3RuZXRfcHJvYl9yZXN1bHRbMToxODMwLF0kUmFtZG9tRm9yZXN0LG5jb2w9MzAsbnJvdz02MSkKCm1kZjwtYXMuZGF0YS5mcmFtZShrbm5fbSkKbWRmPC1jYmluZChtZGYsaWQ9c2VxKDE6NjEpKQptZGY8LXJlc2hhcGUyOjptZWx0KG1kZixpZC52YXJzPWMoImlkIikpCmgxPC1nZ3Bsb3QobWRmKSsKICBnZW9tX3RpbGUoYWVzKHg9aWQseT12YXJpYWJsZSxmaWxsPXZhbHVlKSwKICAgICAgICAgICAgY29sb3VyID0gIndoaXRlIikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIiwKICAgIGhpZ2ggPSAib3JhbmdlIikreWxhYigiIikreGxhYigiIikrCiAgZ3VpZGVzKGZpbGw9RkFMU0UpCgoKbWRmPC1hcy5kYXRhLmZyYW1lKHN2bV9tKQptZGY8LWNiaW5kKG1kZixpZD1zZXEoMTo2MSkpCm1kZjwtcmVzaGFwZTI6Om1lbHQobWRmLGlkLnZhcnM9YygiaWQiKSkKaDI8LWdncGxvdChtZGYpKwogIGdlb21fdGlsZShhZXMoeD1pZCx5PXZhcmlhYmxlLGZpbGw9dmFsdWUpLAogICAgICAgICAgICBjb2xvdXIgPSAid2hpdGUiKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAid2hpdGUiLAogICAgaGlnaCA9ICJvcmFuZ2UiKSt5bGFiKCIiKSt4bGFiKCIiKSsKICBndWlkZXMoZmlsbD1GQUxTRSkKCgptZGY8LWFzLmRhdGEuZnJhbWUocmZfbSkKbWRmPC1jYmluZChtZGYsaWQ9c2VxKDE6NjEpKQptZGY8LXJlc2hhcGUyOjptZWx0KG1kZixpZC52YXJzPWMoImlkIikpCmgzPC1nZ3Bsb3QobWRmKSsKICBnZW9tX3RpbGUoYWVzKHg9aWQseT12YXJpYWJsZSxmaWxsPXZhbHVlKSwKICAgICAgICAgICAgY29sb3VyID0gIndoaXRlIikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIiwKICAgIGhpZ2ggPSAib3JhbmdlIikreWxhYigiIikreGxhYigiIikrCiAgZ3VpZGVzKGZpbGw9RkFMU0UpCgoKbWRmPC1hcy5kYXRhLmZyYW1lKG5iX20pCm1kZjwtY2JpbmQobWRmLGlkPXNlcSgxOjYxKSkKbWRmPC1yZXNoYXBlMjo6bWVsdChtZGYsaWQudmFycz1jKCJpZCIpKQpoNDwtZ2dwbG90KG1kZikrCiAgZ2VvbV90aWxlKGFlcyh4PWlkLHk9dmFyaWFibGUsZmlsbD12YWx1ZSksCiAgICAgICAgICAgIGNvbG91ciA9ICJ3aGl0ZSIpICsKICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsCiAgICBoaWdoID0gIm9yYW5nZSIpK3lsYWIoIiIpK3hsYWIoIiIpKwogIGd1aWRlcyhmaWxsPUZBTFNFKQpncmlkLmFycmFuZ2UoaDEsaDIsaDMsaDQsbmNvbD0yLG5yb3c9MikKYGBgCgojIyMjIGRpZmZlcmVuY2UgaGVhdG1hcHMKYGBge3IgaGVhdG1hcCBkaWZmfQptZGY8LWFzLmRhdGEuZnJhbWUocmZfbSAtIGtubl9tKQptZGY8LWNiaW5kKG1kZixpZD1zZXEoMTo2MSkpCm1kZjwtcmVzaGFwZTI6Om1lbHQobWRmLGlkLnZhcnM9YygiaWQiKSkKbWRmPC1jYmluZChtZGYsc3ViY2xhc3M9KGJvdG5ldF9wcm9iX3Jlc3VsdFsxOjE4MzAsXSRzdWJjbGFzcykpCmRpZmY8LWdncGxvdChtZGYpKwogIGdlb21fdGlsZShhZXMoeD1pZCx5PXZhcmlhYmxlLGZpbGw9dmFsdWUsdGV4dD1zdWJjbGFzcyksCiAgICAgICAgICAgIGNvbG91ciA9ICJ3aGl0ZSIpICsKICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzPWMoInJlZCIsIndoaXRlIiwiZ3JlZW4iKSwKICAgICAgICAgICB2YWx1ZXMgID0gcmVzY2FsZShjKG1pbihtZGYkdmFsdWUpLCAwLjA1LCBtYXgobWRmJHZhbHVlKSkpKSsKICAgICAgICAgICBndWlkZXMoZmlsbD1GQUxTRSkrdGhlbWVfYncoKSAgCmdncGxvdGx5KGRpZmYpCmQzaGVhdG1hcChyZl9tIC0ga25uX20sY29sb3JzID0gIkJsdWVzIixjZWxsbm90ZT1tYXRyaXgoYm90bmV0X3Byb2JfcmVzdWx0WzE6MTgzMCxdJHN1YmNsYXNzLG5jb2w9MzAsbnJvdz02MSkpCmBgYAoKIyMjIENsdXN0ZXJpbmcgYW5kIFBDQQpgYGB7ciBjbHVzdGVyaW5nfQoKa21lYW5zX21vZDwta21lYW5zKHRlc3RpbmdbLDE6MTBdLGNlbnRlcnMgPSAxMCkKdGVzdGluZ19jbHVzdGVyPC1jYmluZCh0ZXN0aW5nLGNsdXN0ZXI9a21lYW5zX21vZCRjbHVzdGVyKQpwY2E8LXByY29tcCh0ZXN0aW5nWyxjKC0xMSwtMTIpXSwgY2VudGVyID0gVFJVRSwgc2NhbGUuID0gVFJVRSkgCnBjYV90ZXN0aW5nPC1kYXRhLmZyYW1lKHBjYSR4LGNsYXNzPXRlc3RpbmdfY2x1c3RlciRjbGFzcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ViY2xhc3M9dGVzdGluZ19jbHVzdGVyJHN1YmNsYXNzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcj10ZXN0aW5nX2NsdXN0ZXIkY2x1c3RlcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQpnPC1nZ3Bsb3QocGNhX3Rlc3RpbmcsYWVzKHg9UEMxLHk9UEMyKSkrCiAgZ2VvbV9qaXR0ZXIoYWVzKGNvbG9yPWFzLmZhY3RvcihzdWJjbGFzcyksdGV4dD1jbHVzdGVyLHNoYXBlPWNsYXNzKSkrCiAgI2dlb21fcG9pbnQoYWVzKHNoYXBlPWFzaWduYWNpb24pLHNpemU9MykrCiAgeWxhYigiUEMxIikreGxhYigiUEMyIikrCiAgdGhlbWVfY2xhc3NpYygpKwojc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jKDgsNikpKwogICBndWlkZXMoY29sb3I9RkFMU0UsYWxwaGE9RkFMU0UpCmdncGxvdGx5KGcpCgpgYGAKCmBgYHtyfQojTm9ybWFsIHByb2JhYmlsaXN0aWMgdGFibGUKbm9ybWFsX3Byb2JfcmVzdWx0ID0gZGF0YS5mcmFtZSh0ZXN0aW5nJGNsYXNzLCBwcmVkc3JmcHJvYnMkTm9ybWFsLCBrbm5QcmVkaWN0JE5vcm1hbCwgbG9naWNSUHJlZGljdCROb3JtYWwsIG5haXZlQmF5ZXNQcmVkaWN0JE5vcm1hbCAsc3ZtUHJlZGljdCROb3JtYWwpCm5hbWVzKG5vcm1hbF9wcm9iX3Jlc3VsdCkgPSBjKCdUcnVlIENsYXNzJywnUmFtZG9tIEZvcmVzdCcsJ0tOTicsJ0xvZ2lzdGljIFJlZ3Jlc3Npb24nLCAnTmFpdmUgQmF5ZXMnLCAnU3Vwb3J0IFZNJykKbm9ybWFsX3Byb2JfcmVzdWx0CmBgYAoK